/* Copyright 2012 Aguzzi Cristiano

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
package com.jme3.gde.gui.multiview.java2d;

import com.jme3.gde.gui.nbeditor.controller.GUIEditor;
import com.jme3.gde.gui.nbeditor.event.SimpleNiftyEditorEvent;
import com.jme3.gde.gui.nbeditor.model.Types;
import com.jme3.gde.gui.nbeditor.controller.selection.GuiSelectionListener;
import com.jme3.gde.gui.nbeditor.renderUtil.SoudDevicenull;
import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.java2d.input.InputSystemAwtImpl;
import de.lessvoid.nifty.java2d.renderer.FontProviderJava2dImpl;
import de.lessvoid.nifty.java2d.renderer.GraphicsWrapper;
import de.lessvoid.nifty.java2d.renderer.RenderDeviceJava2dImpl;
import de.lessvoid.nifty.tools.TimeProvider;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferStrategy;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * J2DNiftyView . Display Nifty in Java2D style. Simple input interactions are
 * implemented
 *
 * @author cris and atomix
 */
public class J2DNiftyView extends javax.swing.JPanel implements GraphicsWrapper, Observer {

    private final byte DIR_N = 0;
    private final byte DIR_E = 1;
    private final byte DIR_S = 2;
    private final byte DIR_W = 3;
    private final byte DIR_SE = 4;
    private final byte NOP = -1;
    // cut short
    private GUIEditor manager;
    protected Nifty nifty;
    // For drawing in Java2D canvas
    private Canvas canvas;
    private Graphics2D graphics2D;
    // timing
    private long fps = 0;
    long time;
    long frames = 0;
    // For element selection
    private boolean dragging, selecting;
    private GuiSelectionListener guiSelectionHandler;
    private Rectangle selectedRect;
    private byte curDir;
    // For handle transform of element
    AffineTransform transformer = new AffineTransform();
    // For testing
    //private Robot mouseBot;
    private long NORMAL_RMS = 15;
    // For viewing
    double zoomX = 1;
    double zoomY = 1;
    BasicStroke stroke = new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 30, new float[]{10.0f, 8.0f}, 0);

    /**
     * Creates new form J2DNiftyView
     */
    public J2DNiftyView(int width, int height) {
        initComponents();
        this.setSize(new Dimension(width, height));

        // For element selection
        guiSelectionHandler = null;

        dragging = false;
        selecting = false;
        curDir = -1;

        this.selectedRect = new Rectangle();
        /*
         try {
         mouseBot = new Robot();
         } catch (AWTException ex) {
         Logger.getLogger(J2DNiftyView.class.getName()).log(Level.SEVERE, null, ex);
         }
         */
    }

    public void createBuffer() {
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        popUp = new javax.swing.JPopupMenu();
        DeleteButton = new javax.swing.JMenuItem();
        HideButton = new javax.swing.JMenuItem();
        NormalizeButton = new javax.swing.JMenuItem();
        jScrollBar1 = new javax.swing.JScrollBar();
        jScrollBar2 = new javax.swing.JScrollBar();

        DeleteButton.setText("Delete");
        DeleteButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                DeleteButtonActionPerformed(evt);
            }
        });
        popUp.add(DeleteButton);

        HideButton.setText("Hide");
        HideButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                HideButtonActionPerformed(evt);
            }
        });
        popUp.add(HideButton);

        NormalizeButton.setText("NormalizeSize");
        NormalizeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                NormalizeButtonActionPerformed(evt);
            }
        });
        popUp.add(NormalizeButton);

        setLayout(new java.awt.BorderLayout());

        jScrollBar1.setOrientation(javax.swing.JScrollBar.HORIZONTAL);
        add(jScrollBar1, java.awt.BorderLayout.PAGE_END);
        add(jScrollBar2, java.awt.BorderLayout.LINE_END);
    }// </editor-fold>//GEN-END:initComponents

    private void HideButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_HideButtonActionPerformed
        this.manager.getElementEditor().setVisibile(false);
        this.selecting = false; //clear the screen from selection
    }//GEN-LAST:event_HideButtonActionPerformed

    private void DeleteButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_DeleteButtonActionPerformed
        this.manager.removeSelected();
        this.selecting = false;
    }//GEN-LAST:event_DeleteButtonActionPerformed

    private void NormalizeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_NormalizeButtonActionPerformed
        this.manager.getElementEditor().normalizeSize();
    }//GEN-LAST:event_NormalizeButtonActionPerformed
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenuItem DeleteButton;
    private javax.swing.JMenuItem HideButton;
    private javax.swing.JMenuItem NormalizeButton;
    private javax.swing.JScrollBar jScrollBar1;
    private javax.swing.JScrollBar jScrollBar2;
    private javax.swing.JPopupMenu popUp;
    // End of variables declaration//GEN-END:variables

    @Override
    public Graphics2D getGraphics2d() {
        return this.graphics2D;
    }

    private void registerFonts(FontProviderJava2dImpl fontProvider) {
        fontProvider.addFont("aurulent-sans-16.fnt", new Font("aurulent-sans-16",
                Font.ROMAN_BASELINE, 16));

    }

    public void init() {
        // createCanvas
        canvas = new Canvas() {
            public void addNotify() {
                super.addNotify();
                createBufferStrategy(2);
            }
        };
        canvas.setSize(this.getSize());

        this.add(canvas, BorderLayout.EAST);
        // this.setMaximumSize(new Dimension(this.getWidth(),this.getHeight()));
        this.setIgnoreRepaint(true);
        canvas.setIgnoreRepaint(true);
        this.jScrollBar1.setVisibleAmount(60);


    }

    public Nifty createNiftyInstance() {
        //FIXME: Fix the way we create Nifty from services!
        InputSystemAwtImpl inputSystem = new InputSystemAwtImpl();
        FontProviderJava2dImpl fontProvider = new FontProviderJava2dImpl();
        registerFonts(fontProvider);

        RenderDeviceJava2dImpl renderDevice = new RenderDeviceJava2dImpl(this);
        renderDevice.setFontProvider(fontProvider);

        nifty = new Nifty(renderDevice, new SoudDevicenull(), inputSystem, new TimeProvider());

        return nifty;
    }

    public void createEventListeners() {

        //canvas.addMouseMotionListener(inputSystem);
        //canvas.addMouseListener(inputSystem);
        //canvas.addKeyListener(inputSystem);
        // canvas.createBufferStrategy(2);
        //  canvas.addMouseMotionListener(this);
        //  canvas.addMouseListener(this);

        this.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                super.componentResized(e);
                canvas.setSize(J2DNiftyView.this.getSize());
            }
        });
    }

    public void start() {

        boolean done = false;
        time = System.currentTimeMillis();
        // this.canvas.setSize((int)(this.getHeight()*1), (int)(this.getWidth()*1));
        /*
         Font fpsFont = new Font("arial", Font.BOLD, 14);
         String fps = "Fps: 0";
         */
        // FIXME: Bind this NiftyView as an obverser of GUIEditor
        this.manager.addObserver(this);

        while (!done) {
            try {
                done = render();
                try {
                    Thread.sleep(NORMAL_RMS);
                } catch (InterruptedException ex) {
                    Logger.getLogger(J2DNiftyView.class
                            .getName()).log(Level.SEVERE, null, ex);
                }

            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }

        }
    }

    @Override
    public int getWidth() {
        return this.canvas.getWidth();
    }

    @Override
    public int getHeight() {
        return this.canvas.getHeight();
    }

    public Nifty getNifty() {
        return nifty;
    }

    public void clearNifty() {
        Nifty temp = this.getNifty();
        for (String sel : temp.getAllScreensName()) {
            temp.removeScreen(sel);
        }
    }

    public void setManager(GUIEditor manager) {
        this.manager = manager;
        this.manager.bindSelectionView(this, popUp);

    }

    protected void setClickListener(GuiSelectionListener guiSelectionListener) {

        //this.canvas.removeMouseListener(guiSelectionHandler);
        //this.canvas.removeMouseMotionListener(guiSelectionHandler);

        this.canvas.addMouseListener(guiSelectionListener);
        this.canvas.addMouseMotionListener(guiSelectionListener);
        this.canvas.addKeyListener(guiSelectionListener);

        guiSelectionHandler = guiSelectionListener;
    }

    public void newGui(GUIEditor guiEditor) {
        this.manager = guiEditor;
        this.setClickListener(guiEditor.getGuiSelectionListener());
        this.selecting = false;
    }

    @Override
    public void update(Observable o, Object arg) {

        SimpleNiftyEditorEvent act = (SimpleNiftyEditorEvent) arg;

        if (act.getType() == SimpleNiftyEditorEvent.SEL && !act.getGUIElement().getType().equals(Types.LAYER)) {
            this.selectedRect.setBounds(act.getGUIElement().getBounds());
            this.selecting = true;
            Logger.getLogger(J2DNiftyView.class.getName()).log(Level.INFO, "Get notified by selection");

        } else if (act.getType() == SimpleNiftyEditorEvent.NEW) {
            this.newGui(((GUIEditor) o));

            o.addObserver(this.guiSelectionHandler);
        } else {
            this.selecting = false;
        }
    }

    public void moveRect(int x, int y) {
        if (selectedRect != null) {
            int dx = (int) (x - this.selectedRect.getCenterX());
            int dy = (int) (y - this.selectedRect.getCenterY());
            this.selectedRect.translate(dx, dy);
        }
    }

    public void displayRect(int x, int y, int h, int w) {
        this.selectedRect.setBounds(x, y, w, h);
        this.selecting = true;
    }

    public void cancelRect() {
        this.selecting = false;
    }

    private boolean render() {

        boolean done = false;
        BufferStrategy bufferStrategy = canvas.getBufferStrategy();

        if (bufferStrategy == null) {
            // FIXME: Potentional Dangerous! Continue with thread confusion even buffer Strategy and other AWT related stuff were not completed
            return false;
        }

        graphics2D = (Graphics2D) bufferStrategy.getDrawGraphics();
        graphics2D.scale(zoomX, zoomY);
        graphics2D.setBackground(Color.BLACK);

        done = nifty.update();
        nifty.render(true);
        //nifty.getRenderEngine().renderQuad(0, 0, 0, 0);

        if (nifty.isDebugOptionPanelColors()) {
            // FPS:
            //graphics2D.setColor(java.awt.Color.red);
            //graphics2D.setFont(fpsFont);
            //graphics2D.drawString(fps, 0, fpsFont.getSize());
        }

        if (selecting) {
            graphics2D.drawLine((int) selectedRect.getCenterX() - 10, (int) selectedRect.getCenterY(), (int) selectedRect.getCenterX() + 10, (int) selectedRect.getCenterY());
            graphics2D.drawLine((int) selectedRect.getCenterX(), (int) selectedRect.getCenterY() - 10, (int) selectedRect.getCenterX(), (int) selectedRect.getCenterY() + 10);

            graphics2D.setColor(java.awt.Color.red);
            graphics2D.setStroke(stroke);
            graphics2D.draw(selectedRect);
            graphics2D.setColor(java.awt.Color.black);
            graphics2D.setStroke(new BasicStroke());
            graphics2D.drawRect((int) selectedRect.getMaxX() - 6, (int) selectedRect.getMaxY() - 6, 11, 11);

            nifty.getRenderEngine().renderQuad((int) selectedRect.getMaxX() - 5, (int) selectedRect.getMaxY() - 5, 10, 10);

        }
        graphics2D.setTransform(transformer);

        bufferStrategy.show();

        graphics2D.dispose();
        graphics2D = null;


        return done;
    }

    public void calculateFPS() {
        /*
         frames++;

         long diff = System.currentTimeMillis() - time;
         if (diff >= 1000) {

         time += diff;
         String fpsText = "Fps: " + frames;
         frames = 0;
         }
         */
    }
    public float ZOOM_AMOUNT = 1.2f;
    /* For viewing */

    public void zoomIn() {
        zoomX *= ZOOM_AMOUNT;
        zoomY *= ZOOM_AMOUNT;
    }

    public void zoomIn(float amount) {
        zoomX *= amount;
        zoomY *= amount;
    }

    public void zoomOut() {
        zoomX /= ZOOM_AMOUNT;
        zoomY /= ZOOM_AMOUNT;
    }

    public void zoomOut(float amount) {
        zoomX *= amount;
        zoomY *= amount;
    }

    public void zoomReset() {
        zoomX = 1;
        zoomY = 1;
    }
    
    public void setViewPort(Rectangle viewRect){
        
    }
}
